import java.util.Arrays;

public class Test {

    public static void main4(String[] args) {
        //为什么java要将数组归为引用类型?
        //因为数组arr中arr是一个引用变量(存放在虚拟机栈) 他指向数组对象{1,2,3}(存放在堆区)
        int[] arr = new int[]{1, 2, 3};
        System.out.println(arr);//[I@776ec8df
        double[] douArr = new double[]{1.1, 1.2, 1.3};
        System.out.println(douArr);//[D@4eec7777

        //arr中存储的是数组对象{1,2,3}的地址经过哈希后的值 同时他又像一个智能指针
        //当一个数组对象没有被引用后就被自动释放了

        arr = new int[]{10, 20, 30};//{1,2,3}被系统释放了
        //不像C 不需要程序员自己free 不会有内存泄漏的问题
        System.out.println(Arrays.toString(arr));//[10, 20, 30]

        getArray(arr);
        System.out.println(Arrays.toString(arr));//[10, 20, 30]
        //为啥arr还是10 20 30? 因为是将arr这个引用赋给 array
        //在getArray中改变的是形参array而不是实参arr

        changeSecond(arr);
        System.out.println(Arrays.toString(arr));//[10, 666, 30]
        //那为啥这里的 20 通过changeSecond改变了呢? 因为形参array这个引用也指向数组对象{10,20,30}
        //因此通过array 访问并修改数组对象就很正常 就像c中的数组一样 通过数组名可以访问到数组元素
        //但想通过数组名来改变数组本身就很麻烦(至少目前解决不了)

        //当我们不知道给引用赋啥值时 可以赋成null
        int[] nums = null;
        //java中的null不同于C中的NULL 他和地址0之间没有直接的联系
        //int[] arr1 = 0;//报错 0是基本类型 而arr1是引用类型

        //一个为null的引用不能对其进行访问
        //System.out.println(nums.length);//报错
        //NullPointerException: Cannot read the array length because "nums" is null
        //System.out.println(nums[0]);//报错
        //NullPointerException: Cannot load from int array because "nums" is null

        //数组的应用场景主要有：1.保存数据 2.作为函数的参数 3.作为函数的返回值
        //通过getFibs获得斐波那契数列的前n项
        System.out.println(Arrays.toString(getFibs(10)));
        //[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

    }
    public static int[] getFibs(int n){
        int[] fibs=new int[n];
        fibs[0]=fibs[1]=1;
        int f1=1,f2=1,f3=1;
        for(int i=3;i<=n;i++){
            f3=f1+f2;
            fibs[i-1]=f3;
            f1=f2;
            f2=f3;
        }
        return fibs;
    }

    public static void getArray(int[] array) {
        array = new int[10];
    }

    public static void changeSecond(int[] array) {
        array[1] = 666;
    }

    //P37作业: 7 8
    public static void printArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
        //System.out.println(Arrays.toString(arr));
    }

    public static void main3(String[] args) {
        //创建一个有100个元素的数组且每个元素依次为1 2 3 ... 100
        int[] arr = new int[100];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i + 1;
        }
        //打印arr
        System.out.println(Arrays.toString(arr));
        /*for (int x : arr) {
            System.out.print(x + " ");
        }*/
        printArr(arr);
    }

    public static void main2(String[] args) {
        //java中的数组不是基本类型而是属于引用类型 指向的对象存储在堆区
        //定义数组的语法：
        // 1.元素的数据类型[] 数组名 = {元素1,元素2,...};
        // 2.元素的数据类型[] 数组名 = new 元素的数据类型[]{元素1,元素2,...};
        // 3.元素的数据类型[] 数组名 = new 元素的数据类型[元素个数];
        int[] arr1 = {1, 2, 3, 4};
        int[] arr2 = new int[]{1, 2, 3, 4};
        int[] arr3 = new int[10];//各元素被初始化为0
        //int[] arr3 =new int[3]{1,2,3};//报错 不能即指定大小又初始化
        int[] arr4;//可以不指定数组的大小
        arr4 = new int[]{1, 2, 3};
        //arr4={1,2,3};//报错

        //同样数组可以用下标访问 并可以用数组名.length得到数组的长度(元素个数)
        for (int i = 0; i < arr4.length; i++) {
            System.out.print(arr4[i] + " ");
        }
        //当然万不可越界访问
        //System.out.println(arr4[10]);
        //ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3

        System.out.println();
        for (int i = 0; i < arr4.length; i++) {
            arr4[i] += i;//同样可以通过下标来修改对应元素
        }

        //foreach 语法：for(遍历的数组的数据类型 变量名: 需要遍历的数组名){
        //需要进行的操作
        //}

        for (int x : arr4) {
            System.out.print(x + " ");
        }//输出 1 3 5
        for (int n :
                arr4) {
            n += 3;//只是将arr4中的每个元素赋值给n 并不能通过n来修改arr4
        }
        System.out.println();
        for (int x : arr4) {
            System.out.print(x + " ");
        }
        System.out.println();
        //通过Arrays.toString来输出arr4
        String arr4Str = Arrays.toString(arr4);
        System.out.println(arr4Str);//[1, 3, 5]

    }

    public static void main1(String[] args) {
        //递归：一个问题可以分解为有限个子问题的解的 "和"
        //1.明确递归结束条件(基础情况) 2.找出递归公式

        //print(98765422);
        //System.out.println(sumNum(123));
        System.out.println(fac(5));
        /*输出
          fac(n=5)开始
          fac(n=4)开始
          fac(n=3)开始
          fac(n=2)开始
          fac(n=1)开始
          fac(n=1)结束 返回1
          fac(n=2)结束 返回2
          fac(n=3)结束 返回6
          fac(n=4)结束 返回24
          fac(n=5)结束 返回120
          120
         */
        hanoi(1, 'A', 'B', 'C');
        System.out.println();
        hanoi(2, 'A', 'B', 'C');
        System.out.println();
        hanoi(3, 'A', 'B', 'C');
        System.out.println();

    }

    //输出移动路程
    public static void move(char sour, char dest) {
        System.out.print(sour + "->" + dest + " ");
    }

    //汉诺塔                   盘子总数   起始位置  中转位置    目的地
    public static void hanoi(int n, char sour, char trans, char dest) {
        if (n == 1) {
            move(sour, dest);
            return;
        }
        hanoi(n - 1, sour, dest, trans);
        move(sour, dest);
        hanoi(n - 1, trans, sour, dest);
    }

    //顺序打印一个正整数的每一位数
    public static void print(int n) {
        if (n / 10 == 0) {
            System.out.print(n + " ");
            return;
        }
        print(n / 10);
        System.out.print(n % 10 + " ");
    }

    //求一个正整数的每一位数的和
    public static int sumNum(int n) {
        if (n / 10 == 0) {
            return n;
        }
        return (sumNum(n / 10) + n % 10);
    }

    //递归求n! 了解递归过程
    public static long fac(int n) {
        System.out.println("fac(n=" + n + ")开始");
        if (n == 1) {
            System.out.println("fac(n=1)结束 返回1");
            return 1;
        }
        long ret = fac(n - 1) * n;
        System.out.println("fac(n=" + n + ")结束 返回" + ret);
        return ret;
    }
}
